/*  reducer_ptr.h                  -*-C++-*-
 *
 * Copyright (C) 2011, Intel Corporation
 * All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 
 *   * Redistributions of source code must retain the above copyright
 *     notice, this list of conditions and the following disclaimer.
 *   * Redistributions in binary form must reproduce the above copyright
 *     notice, this list of conditions and the following disclaimer in
 *     the documentation and/or other materials provided with the
 *     distribution.
 *   * Neither the name of Intel Corporation nor the names of its
 *     contributors may be used to endorse or promote products derived
 *     from this software without specific prior written permission.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
 * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY
 * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *
 */

#ifndef CILK_REDUCER_PTR_H_INCLUDED
#define CILK_REDUCER_PTR_H_INCLUDED

#include "cilk/reducer.h"
#include "tbb/atomic.h"

#ifdef __cplusplus

#ifndef CILK_STUB

namespace cilk {
namespace internal {

// Metafunction to return the value_type for a specific view_type
template <class View, bool has_nested_value>
struct view_to_value_imp;

// Specialization returning view_to_value for views that define a nested
// value_type.
template <class View>
struct view_to_value_imp<View, true>
{
    typedef typename View::value_type type;

    // Return value reference for a specific view.
    static type& get_value_ref_for_current_strand(View& r)
    {
        return r.get_value_ref();
    }
};

// Specialization returning value_type for views that don't define nested a
// value_type.
template <class View>
struct view_to_value_imp<View, false>
{
    typedef View type;

    // Return value reference for a specific view.
    static type& get_value_ref_for_current_strand(View& r)
    {
        return r;
    }
};

// Metafunction to return the value_type for a specific view type.  If the
// View class has a nested value_type typedef, then type will be
// View::value_type.  Otherwise, type will be View.
template <class View>
class view_to_value
{
    template <class T> static char test(T*, typename T::value_type*);
    template <class T> static int  test(T*, ...);

public:
    typedef typename
       view_to_value_imp<View, 1 == sizeof(test((View*) 0, 0))>::type type;

    // Return value reference for a specific view.
    static type& get_value_ref_for_current_strand(View& r)
    {
        return view_to_value_imp<View, 1 == sizeof(test((View*) 0, 0))>::
            get_value_ref_for_current_strand(r);
    }
};

// Metafunction to return the view_type for a specific monoid
template <class Monoid, bool has_view>
struct monoid_view_type_imp;

// Specialization returning view_type for monoids that define view_type
template <class Monoid>
struct monoid_view_type_imp<Monoid, true>
{
    typedef typename Monoid::view_type type;
};

// Specialization returning value_type for monoids that don't define view_type
template <class Monoid>
struct monoid_view_type_imp<Monoid, false>
{
    typedef typename Monoid::value_type type;
};

// Metafunction to return the view_type for a specific monoid.  If the
// Monoid class has a nested view_type, then type will be view_type.
// Otherwise, type will be value_type.
template <class Monoid>
class monoid_view_type
{
    template <class T> static char test(T*, typename T::view_type*);
    template <class T> static int  test(T*, ...);

public:
    typedef typename
        monoid_view_type_imp<Monoid,
                             1 == sizeof(test((Monoid*) 0, 0))>::type type;
};

// Representation object for a reducer_ptr.  This object is an old-style
// reducer object pointed to by a reducer_ptr.  The reducer_ptr_rep is
// reference-counted, and is destroyed when the last reducer_ptr referencing
// it is destroyed.
template <class Monoid>
class reducer_ptr_rep
{
public:
    typedef typename Monoid::value_type             value_type;
    typedef typename monoid_view_type<Monoid>::type view_type;

private:
    // Components of a C reducer
    __cilkrts_hyperobject_base  m_base;
    Monoid                      m_monoid; // implementation of monoid interface

    tbb::atomic<int>            m_refcount;

    // Wrappers around C monoid dispatch functions
    static void reduce_wrapper(void* r, void* lhs, void* rhs);
    static void identity_wrapper(void* r, void* view);
    static void destroy_wrapper(void* r, void* view);
    static void* allocate_wrapper(void* r, __STDNS size_t bytes);
    static void deallocate_wrapper(void* r, void* view);

    void init(view_type* primary_view);

    // No copying
    reducer_ptr_rep(const reducer_ptr_rep&);
    reducer_ptr_rep& operator=(const reducer_ptr_rep&);

  public:
    reducer_ptr_rep(const Monoid& m, value_type* p)
        : m_monoid(m)
        , m_refcount()
    {
        view_type* primary_view = reinterpret_cast<view_type*>(p);
        m_refcount = 1;
        init(primary_view);
    }

    __CILKRTS_STRAND_STALE(~reducer_ptr_rep());

    /* access the unwrapped object */
    view_type* view_ptr() {
        /* look up reducer in current map */
        return static_cast<view_type *>(__cilkrts_hyper_lookup(&m_base));
    }

    int addref() { return ++m_refcount; }
    int subref() { return --m_refcount; }

    Monoid&       monoid()       { return m_monoid; }
    Monoid const& monoid() const { return m_monoid; }
};

template <typename Monoid>
void reducer_ptr_rep<Monoid>::init(view_type* primary_view)
{
    static const cilk_c_monoid c_monoid_initializer = {
        (cilk_c_reducer_reduce_fn_t)     &reduce_wrapper,
        (cilk_c_reducer_identity_fn_t)   &identity_wrapper,
        (cilk_c_reducer_destroy_fn_t)    &destroy_wrapper,
        (cilk_c_reducer_allocate_fn_t)   &allocate_wrapper,
        (cilk_c_reducer_deallocate_fn_t) &deallocate_wrapper
    };

    m_base.__c_monoid = c_monoid_initializer;
    m_base.__flags = 0;
    m_base.__view_offset = (char*) primary_view - (char*) this;
    m_base.__view_size = sizeof(view_type);

    __cilkrts_hyper_create(&m_base);
}

template <typename Monoid>
void reducer_ptr_rep<Monoid>::reduce_wrapper(void* r, void* lhs, void* rhs)
{
    reducer_ptr_rep* self = static_cast<reducer_ptr_rep*>(r);
    self->m_monoid.reduce(static_cast<view_type*>(lhs),
                          static_cast<view_type*>(rhs));
}

template <typename Monoid>
void reducer_ptr_rep<Monoid>::identity_wrapper(void* r, void* view)
{
    reducer_ptr_rep* self = static_cast<reducer_ptr_rep*>(r);
    self->m_monoid.identity(static_cast<view_type*>(view));
}

template <typename Monoid>
void reducer_ptr_rep<Monoid>::destroy_wrapper(void* r, void* view)
{
    reducer_ptr_rep* self = static_cast<reducer_ptr_rep*>(r);
    self->m_monoid.destroy(static_cast<view_type*>(view));
}

template <typename Monoid>
void* reducer_ptr_rep<Monoid>::allocate_wrapper(void* r,
                                                   __STDNS size_t bytes)
{
    reducer_ptr_rep* self = static_cast<reducer_ptr_rep*>(r);
    return self->m_monoid.allocate(bytes);
}

template <typename Monoid>
void reducer_ptr_rep<Monoid>::deallocate_wrapper(void* r, void* view)
{
    reducer_ptr_rep* self = static_cast<reducer_ptr_rep*>(r);
    self->m_monoid.deallocate(static_cast<view_type*>(view));
}

template <typename Monoid>
__CILKRTS_STRAND_STALE(reducer_ptr_rep<Monoid>::~reducer_ptr_rep())
{
    __cilkrts_hyper_destroy(&m_base);
}

} // end namespace cilk::internal

/// Return the value object contained within the specified view.
template <typename View>
inline
typename internal::view_to_value<View>::type&
get_value_ref_for_current_strand(View& v)
{
    return internal::view_to_value<View>::get_value_ref_for_current_strand(v);
}

template <typename View>
inline
typename internal::view_to_value<View>::type const&
get_value_ref_for_current_strand(const View& v)
{
    return get_value_ref_for_current_strand(const_cast<View&>(v));
}

template <typename View>
inline
typename internal::view_to_value<View>::type
get_value_for_current_strand(const View& v)
{
    return get_value_ref_for_current_strand(v);
}

template <typename View>
inline
void set_value_for_current_strand(View& v,
                    const typename internal::view_to_value<View>::type& val)
{
    get_value_ref_for_current_strand(v) = val;
}

/// The 'monoid_base2' class template is a useful base class for a large set
/// of monoid classes for which the identity value is a default-constructed
/// value of type 'View', allocated using operator new.  A derived class of
/// 'monoid_base2' need only declare and implement the 'reduce' function.
template <class View>
class monoid_base2 : public monoid_base<View>
{
public:
    /// Type of value and view for this monoid
    typedef View                                         view_type;
    typedef typename internal::view_to_value<View>::type value_type;
};

template <typename Monoid>
class reducer_ptr
{
    typedef internal::reducer_ptr_rep<Monoid> shared_block;
    shared_block* m_rep;

public:
    typedef typename shared_block::value_type value_type;
    typedef typename shared_block::view_type  view_type;
    
    explicit reducer_ptr(value_type* p = 0)
        : m_rep(p ? new shared_block(Monoid(), p) : 0) { }
    explicit reducer_ptr(const Monoid& m, value_type* p = 0)
        : m_rep(p ? new shared_block(m, p) : 0) { }

    reducer_ptr(const reducer_ptr& other)
        : m_rep(other.m_rep) { if (m_rep) m_rep->addref(); }

    reducer_ptr& operator=(const reducer_ptr& other) {
        if (other.m_rep) other.m_rep->addref();
        if (m_rep && 0 == m_rep->subref()) delete m_rep;
        m_rep = other.m_rep;
        return *this;
    }

#ifdef __CILKRTS_RVALUE_REFERENCES
    reducer_ptr(reducer_ptr&& other)
        : m_rep(other.m_rep) { other.m_rep = 0; }

    reducer_ptr& operator=(reducer_ptr&& other) {
        shared_block* tmp = m_rep;
        m_rep = other.m_rep;
        other.m_rep = tmp;
        return *this;
    }
#endif

    ~reducer_ptr() { if (m_rep && (0 == m_rep->subref())) delete m_rep; }

    view_type* view_ptr() const { return m_rep ? m_rep->view_ptr() : 0; }
    view_type& view_ref() const { return *m_rep->view_ptr(); }

    value_type* value_ptr() const {
        return reinterpret_cast<value_type*>(view_ptr());
    }
    value_type& value_ref() const {
        return reinterpret_cast<value_type&>(view_ref());
    }

    view_type& operator*() const { return view_ref(); }
    view_type* operator->() const { return view_ptr(); }

    const Monoid& monoid() const { return m_rep->monoid(); }
};

} // end namespace cilk

#endif // CILK_STUB

#else /* C interface */
#endif /* C interface */

#endif /* CILK_REDUCER_PTR_H_INCLUDED */
